home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998…eptember: Technology Seed / September 98 ADC Seed CD.toast / LaserWriter 8.6b5 Seed / Prerelease Docs / sample JPEGConverter / Source / sample JPEGConverterLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  36.9 KB  |  1,257 lines  |  [TEXT/MPS ]

  1. /*
  2. *    File Name:        sample JPEGConverterLib.c
  3. *
  4. *    Description:    This file contains the beginnings of a low level JPEG downloader.
  5. *
  6. *    Written by:        David Gelphman    4/3/98
  7. *
  8. *    Copyright:    © 1998 by Apple Computer Inc., all rights reserved.
  9. *    
  10. *
  11. *    Change History (most recent first):
  12. *
  13. *        04/03/98    DMG457    Initial version.
  14. */
  15.  
  16. #include <Types.h>
  17. #include <Resources.h>
  18. #include <TextUtils.h>
  19. #include <Errors.h>
  20. #include <FixMath.h>
  21.  
  22. #include "Debug.h"
  23. #include "DownloadMgrLib.h"
  24. #include "sample JPEGConverterLib.h" 
  25. #include "DownloadMgrLowConverter.h"
  26. #include "Version.h"
  27. #include "sample ConverterShell.h"
  28. #include "Utilities.h"
  29. #include "PSWriterErr.h"
  30.  
  31. /********* Defines *********/
  32.  
  33. #define JPEGBUFFERSIZE 0x4000    // size for buffer allocations for reading data to convert
  34.  
  35. #define kNumHandledTypes 3        // number of types that the sample JPEG converter handles
  36.  
  37.  
  38. /*    This sample converter doesn't need to peek but there is code below to demonstrate how we'd save any
  39.     data collected upon peek and how we'd access it later. 
  40. */
  41.  
  42. #define doSamplePeek 0        //To exercise the code, change doSamplePeek to be 1.
  43.  
  44. #if doSamplePeek
  45. /* my typedefs for my private hint data */
  46. typedef Boolean hintMyPrivateVar;
  47. typedef SInt32     hintMyPrivate2Var;
  48.  
  49. /* the tag and id for my private data hints */
  50. #define kHintMyPrivateTag 'tag1'
  51. #define kHintMyPrivateTag2 'tag2'
  52. #define kHintMyPrivateId    1            // both kHintMyPrivateTag, and kHintMyPrivateTag2 will use the same ID
  53.  
  54. #define kHintMyPrivateDefault    false    // my default values for these hints should they not be present
  55. #define kHintMyPrivate2Default    -1
  56.  
  57. /*
  58.     The signature used here is reserved by Apple. This data MUST be your application private signature in
  59.     order to ensure that there are no collisions with other software. 
  60. */
  61. #define myPrivateAppSignatureID    'vgrd'
  62.  
  63. #endif
  64.  
  65.  
  66. /********* Typedefs *********/
  67.  
  68. /* We need these type definitions because ConverterService is declared as:
  69.  
  70.     typedef struct MyConverterService{
  71.         UInt32 nTypes;
  72.         ConverterTypeInfo typeInfo[1];            
  73.     }MyConverterService;
  74.     
  75.     but for this converter we have an array of kNumHandledTypes typeInfo structures in our ConverterService. 
  76.     The Download Manager is expecting this to be an array of an unknown number of ConverterTypeInfo structures,
  77.     but we have to tell the compiler here the correct number so we can initialize "gTheConverterDescription" correctly.
  78. */
  79.  
  80. typedef struct MyConverterService{
  81.     UInt32 nTypes;
  82.     ConverterTypeInfo typeInfo[kNumHandledTypes];            
  83. }MyConverterService;
  84.  
  85. typedef struct MyConverterDescription {
  86.     OSType converterDescSignature;
  87.     ConverterDescVersion converterDescVersion;
  88.     ConverterType converterType;
  89.     MyConverterService converterService;
  90. }MyConverterDescription;
  91.  
  92. // our client data for converting the data
  93. typedef struct JPEGConverterData{
  94.     SInt16 imageheight;
  95.     SInt16 imagewidth;
  96.     SInt16    depth;
  97.     SInt16    numComponents;
  98.     Boolean doBinary;
  99.     Boolean doLevel2;
  100.     unsigned char *outDataBuffer;
  101.     UInt32 outBufferSize;
  102.     unsigned char *backChannelDataBuffer;
  103.     SInt32 backChannelDataBufferSize;
  104.     PSSerialStream *readStream;
  105.     PSStream *streamIn;
  106.     PSStream *streamOut;
  107. }JPEGConverterData;
  108.  
  109. // for ASCII85 handling
  110. typedef struct PackBitsState{
  111.     unsigned long edgeBytes;
  112.     int edgeCount;
  113. }PackBitsState;
  114.  
  115. typedef void (*compressionProcPtr)(Ptr *src, Ptr *dst, long count);
  116.  
  117. /********** Prototypes ************/
  118.  
  119. // JPEG data scanning routines
  120. static char *MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth, long validBytes);
  121. static void SwallowQuantTable(char *data, char *bufferEnd);                    
  122. static void SwallowHuffTable(char *data, char *bufferEnd);                    
  123. static OSStatus GetJPEGImageBounds(unsigned char *theDataPtr, short *width, short *height, short *depth, 
  124.                                         long *hRes, long *vRes, long validDataLength);
  125.  
  126. // ASCII85 output routines
  127. static OSStatus ASCII85Done(StreamInfoData comm, PackBitsState *bitsState);
  128. static OSStatus outASCII85(StreamInfoData comm, PackBitsState *bitsState, unsigned char *block, long nBytes, compressionProcPtr compPtr);
  129. static void  noCompressionASCII85 (Ptr *src, Ptr *dst, long count);
  130. static StringPtr pack5(StringPtr out, unsigned long data);
  131.  
  132.  
  133. /************** Static global data ************/
  134.  
  135. static const unsigned char psPrologL2[] = "\p/s{{/DeviceGray/d/DeviceRGB/DeviceCMYK}components 1 sub get setcolorspace}def\r";
  136.  
  137. static const unsigned char psL2BinaryFilter[] = "\p/myfilter currentfile/DCTDecode filter def\r";
  138.  
  139. static const unsigned char psL2ASCII85Filter[] = "\p/myfilter currentfile/ASCII85Decode filter/DCTDecode filter def\r";
  140.                                         
  141. static const unsigned char psImageDictSetup1[] = "\p/iwidth ^d def/iheight ^d def/components ^d def s\r"
  142.                                         "10 dict dup begin/ImageType 1 def/MultipleDataSource false def\r"
  143.                                         "/Width iwidth def/Height iheight def/Decode[components{0 1}repeat]def\r";
  144.                                         
  145. static const unsigned char psImageDictSetup2[] = "\p/ImageMatrix[iwidth 0 0 iheight neg 0 iheight]def\r"
  146.                                         "/DataSource myfilter def\r"
  147.                                         "/BitsPerComponent 8 def end\r";
  148.  
  149. static const unsigned char psDoImage[] = "\piwidth iheight scale image ";    // this must be terminated with a space, not a CR.
  150.  
  151.  
  152. // the converter description we give to the Download Manager
  153. MyConverterDescription gTheConverterDescription =
  154. {
  155.     // signature info
  156.     kTheConverterDescriptionSignature,     // signature always first
  157.     kInitialConverterDescriptor,         // version
  158.     // type info
  159.     {
  160.         "\pJPEG Downloader",    // name
  161.         "\p",                                                                // info string data comes from resource fork
  162.         kMajorRev, kMinorRev, kReleaseStage, kNonRelease,
  163.     },
  164.  
  165.     // Converter Services
  166.     {
  167.         kNumHandledTypes, // number of ConverterTypeInfo structures
  168.         {
  169.             {
  170.                 'JPEG',             // file type for JPEG data
  171.                 CANTDOWNLOAD,        // priority hint - we can't download if we can't look at 
  172.                                     // more than the first 15 bytes of data to verify it is JPEG data we can handle
  173.                 "\p\xFF\xD8"        // the first two bytes of JPEG/JFIF data
  174.             },
  175.             {
  176.                 'JFIF',             // file type for JFIF data
  177.                 CANTDOWNLOAD,        // priority hint - we can't download if we can't look at 
  178.                                     // more than the first 15 bytes of data to verify it is JPEG data we can handle
  179.                 "\p\xFF\xD8"        // the first two bytes of JPEG/JFIF data
  180.             },
  181.             {
  182.                 '????',             // file type for unknown data
  183.                 CANTDOWNLOAD,        // priority hint - we can't download if we can't look at 
  184.                                     // more than the first 15 bytes of data to verify it is JPEG data we can handle
  185.                 "\p\xFF\xD8"        // the first two bytes of JPEG/JFIF data
  186.             }
  187.         }
  188.     }
  189. };
  190.  
  191. // end of global data
  192.  
  193. OSStatus converterGetConverterInfoPtr(const ConverterDescription **theDescriptionPtr)
  194. {
  195.     OSStatus err = noErr;
  196.     short libRes, holdResFile;
  197.  
  198.     /*    The reason we open the library resource fork here rather than in the library init proc
  199.         is that we are using shared global data so our init proc only is called when the library
  200.         is initially opened. If we were to open the library and save the
  201.         fref, that would work for the caller who made the first call to the library, but any
  202.         subsequent caller into the already opened library wouldn't have the resource
  203.         fork in its resource chain and our resource calls would fail. Instead the shell code
  204.         saves the library FSSpec and we need to open (and close) the library when we need it.
  205.         No big deal but if we don't respect this, we'll have problems when there are multiple
  206.         downloads going on simultaneously.
  207.     */
  208.  
  209.     holdResFile = CurResFile();
  210.     err = openLowLibraryResFile(&libRes);
  211.     if(!err){
  212.         // we get our info string from our resource data since it should be internationalizeable
  213.         GetIndString(gTheConverterDescription.converterType.info, JPEGCONVERTERSTRINGS_ID, kJPEGConverterInfoString);
  214.  
  215.         CloseResFile(libRes);
  216.     }
  217.  
  218.     if(theDescriptionPtr)*theDescriptionPtr = (ConverterDescriptionPtr)&gTheConverterDescription;
  219.     return err;
  220. }
  221.  
  222. OSStatus converterGetConverterName(Str255 converterName)
  223. {
  224.     OSStatus err = noErr;
  225.     short libRes, holdResFile;
  226.     
  227.     /*    The reason we open the library resource fork here rather than in the library init proc
  228.         is that we are using shared global data so our init proc only is called when the library
  229.         is initially opened. If we were to open the library and save the
  230.         fref, that would work for the caller who made the first call to the library, but any
  231.         subsequent caller into the already opened library wouldn't have the resource
  232.         fork in its resource chain and our resource calls would fail. Instead the shell code
  233.         saves the library FSSpec and we need to open (and close) the library when we need it.
  234.         No big deal but if we don't respect this, we'll have problems when there are multiple
  235.         downloads going on simultaneously.
  236.     */
  237.     
  238.     holdResFile = CurResFile();
  239.     err = openLowLibraryResFile(&libRes);
  240.  
  241.     if(!err){
  242.         // we get our converter name from our resource data since it should be internationalizeable
  243.         GetIndString(converterName, JPEGCONVERTERSTRINGS_ID, kJPEGConverterName);    
  244.         CloseResFile(libRes);
  245.     }
  246.     UseResFile(holdResFile);
  247.  
  248.     return err;
  249. }
  250.  
  251. OSStatus converterGetConverterDocType(PSSerialStream *readStream, PSStream *inputStream, Collection hints, OSType *theTypeP)
  252. {
  253.     Unused(readStream);
  254.     Unused(inputStream);
  255.     Unused(hints);
  256.     
  257.     // We don't need to look at the data or the hints collection to know what type of data we have
  258.     // If we get to this point we have our data type
  259.     
  260.     if(theTypeP){
  261.         *theTypeP = kJPEGConverterDocType;
  262.     }
  263.     
  264.     return noErr;
  265. }
  266.  
  267.  
  268. OSStatus converterGetVersion(struct CFMVersion *version)
  269. {
  270.         version->definition = lowJPEGConverterVersionCurrent;
  271.         version->implementation = lowJPEGConverterVersionImplementation;
  272.         version->current = lowJPEGConverterVersionDefinition;
  273.         
  274.         return noErr;
  275. }
  276.  
  277. // no need to add additional queries than the ones the shell code does for us
  278. OSStatus converterAddAdditionalQueries(Collection hints, Collection query)
  279. {
  280.     Unused(hints);
  281.     Unused(query);
  282.  
  283.     return noErr;
  284. }
  285.  
  286. #define CANDOWNLOAD_BUFFER_SIZE     JPEGBUFFERSIZE
  287. OSStatus converterCanConvert(PSSerialStream *readStream, PSStream *stream, Collection hints, LowConverterInfo *dataInfo, Fixed *downloadability)
  288. {
  289.     OSStatus err = noErr;
  290.     unsigned char *theData;
  291.     SInt32 size;
  292.     Boolean needLevel1 = true;
  293.     
  294.     Unused(dataInfo);
  295.     
  296.     *downloadability = CANTDOWNLOAD;            // start by saying we can't download this file
  297.     
  298.     if(!err){
  299.         // check if we know anything about the PostScript language level this job is targetted to
  300.         kHintLanguageLevelVar langlevel = kHintLanguageLevelDef;
  301.         err = getHint(hints, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(langlevel), &langlevel);
  302.         if(err == collectionItemNotFoundErr)err = noErr;
  303.                         
  304.         if(!err){
  305.             // mustDoLevel1 macro lets us know if the language level requires us to do level 1
  306.             needLevel1 = mustDoLevel1(langlevel);
  307.         }
  308.  
  309.     }
  310.     
  311.     // we *might* be able to handle the data if the caller doesn't require Level 1
  312.     if(!err && !needLevel1){
  313.     
  314.         theData = (unsigned char *)NewPtr(CANDOWNLOAD_BUFFER_SIZE);
  315.         size = CANDOWNLOAD_BUFFER_SIZE;
  316.         if(theData){    
  317.             err = readStream->read(stream, (Ptr)(theData), &size);        // call the readStream input to get the first buffer of data
  318.             if(err == eofErr)err = noErr;                                // eof errors are OK. We'll just use the data we got
  319.     
  320.             if(!err){
  321.                 short width, height, depth;
  322.                 long    hRes = 72L<<16 , vRes = 72L<<16;
  323.     
  324.                 err = GetJPEGImageBounds(theData, &width, &height, &depth, &hRes, &vRes, size);    
  325.                 if(!err){
  326.                     if((depth == 8 || depth == 24) && width && height){
  327.                         *downloadability = BESTPRIORITY;
  328.                     }/*
  329.                     else
  330.                         Assert(...);
  331.                     */
  332.                 }
  333.                 err = noErr;            // we can't handle the data, sorry. We don't want to return an error
  334.                                         // if we simply can't handle the data
  335.             }
  336.             psDisposePtr((Ptr)theData);
  337.         }else
  338.             err = noErr;            // we can't handle the data, sorry. We don't want to return any error
  339.                                     // if we simply can't handle the data
  340.     }
  341.             
  342.     return err;
  343. }
  344. #undef CANDOWNLOAD_BUFFER_SIZE
  345.  
  346.  
  347. OSStatus converterPeekConvert(PSSerialStream *readStream, PSStream *inputStream, Collection hints)
  348. {
  349.     OSStatus err = noErr;
  350.     Unused(readStream);
  351.     Unused(inputStream);
  352.     Unused(hints);
  353.     
  354. #if doSamplePeek
  355.     // this code isn't executed but is simply here to demonstrate how a converter would save any peeked data
  356.     // it collected. The tag/id values it uses for its private collection are up to it. 
  357.     if(!err){
  358.         Collection privateHints = NewCollection();
  359.         if(privateHints){
  360.             hintMyPrivateVar myData = kHintMyPrivateDefault;
  361.             err = AddCollectionItem(privateHints, kHintMyPrivateTag, kHintMyPrivateId, sizeof(myData), &myData);
  362.             if(!err){
  363.                 hintMyPrivate2Var myData2 = kHintMyPrivate2Default;
  364.                 err = AddCollectionItem(privateHints, kHintMyPrivateTag2, kHintMyPrivateId, sizeof(myData2), &myData2);
  365.             }
  366.             // ... add all of our hints as we need to    
  367.                 
  368.             // if we have no errors after adding all our hints, then lets add our private hints to the public
  369.             // hints for use later in doConvert
  370.             if(!err){
  371.                 Handle myCollectionHdl = NewHandle(0);
  372.                 if(myCollectionHdl){
  373.                     // we have to flatten our collection before adding it to the hints
  374.                     err = FlattenCollectionToHdl(privateHints, myCollectionHdl);
  375.                     
  376.                     /*     Now add our private hints collection to the hints collection.
  377.                         The ONLY private hint we can add to the 'hints' collection passed in here
  378.                         without possibly colliding with other hints in that collection
  379.                         is a hint with the tag kHintAppPrivateTag and an ID which is the app signature 
  380.                         DTS has assigned us.
  381.                     */
  382.                     if(!err)
  383.                         err = AddCollectionItemHdl(hints, kHintAppPrivateTag, myPrivateAppSignatureID, myCollectionHdl);
  384.                     
  385.                     // dispose of the handle even if there were errors since we allocated it
  386.                     DisposeHandle(myCollectionHdl);
  387.                 }else
  388.                     err = MemError();
  389.             }
  390.             DisposeCollection(privateHints);        // dispose of our collection now that we are done with it
  391.         }else
  392.             err = MemError();
  393.     }    
  394. #endif
  395.                 
  396.     return err;
  397. }
  398.  
  399. #define DOWNLOAD_BUFFER_SIZE     JPEGBUFFERSIZE
  400. OSStatus converterInitDoConvertClientData(void **clientData, PSSerialStream *readStream, PSStream *streamIn,
  401.                     PSStream *streamOut, Collection hints, 
  402.                     unsigned char *backChannelDataBuffer, SInt32 backChannelDataBufferSize,                                        
  403.                     UInt32 *languageLevel, Boolean doBinary, Boolean canDoGrayOnHost,
  404.                     Boolean isNotEPS)
  405. {
  406.     OSStatus err = noErr;
  407.     JPEGConverterData *myData = NULL;
  408.     Unused(hints);
  409.     Unused(isNotEPS);
  410.     Unused(canDoGrayOnHost);
  411.     
  412. // If we'd collected data on peeking, here is how we'd obtain it.
  413. #if doSamplePeek
  414.     // this code isn't executed but is simply here to demonstrate how a converter would save any peeked data
  415.     // it collected. The tag/id values it uses for its private collection are up to it. 
  416.     if(!err){
  417.         /*    here we assign default values for the data we are going to get from our hints. If our
  418.             private collection is not available or our hints aren't available, we want to be able
  419.             to proceed. This would be the case if we didn't get a chance to peek at the data.
  420.         */
  421.         hintMyPrivateVar myData = kHintMyPrivateDefault;
  422.         hintMyPrivate2Var myData2 = kHintMyPrivate2Default;
  423.  
  424.         Collection privateHints = NewCollection();
  425.         if(privateHints){
  426.             Handle myCollectionHdl = NewHandle(0);
  427.             if(myCollectionHdl){
  428.                 // get our flattened collection from our private hint we added. Be prepared for it to not exist!
  429.                 err = GetCollectionItemHdl(hints, kHintAppPrivateTag, myPrivateAppSignatureID, myCollectionHdl);
  430.                 if(!err){
  431.                     // we have to unflatten our collection before we can look at our hints
  432.                     err = UnflattenCollectionFromHdl(privateHints, myCollectionHdl);
  433.                     if(!err){
  434.                         err = getHint(privateHints, kHintMyPrivateTag, kHintMyPrivateId, sizeof(myData), &myData);
  435.                         if(err == collectionItemNotFoundErr){
  436.                             err = noErr;            // we'll use the defaults we set above
  437.                         }
  438.                         if(!err){
  439.                             err = getHint(privateHints, kHintMyPrivateTag2, kHintMyPrivateId, sizeof(myData2), &myData2);
  440.                             if(err == collectionItemNotFoundErr){
  441.                                 err = noErr;            // we'll use the defaults we set above
  442.                             }
  443.                         }
  444.                         // ... get all of our hints as we need to    
  445.                     
  446.                     }
  447.                 }else{
  448.                     // if our private hint is not available. This would happen if peek weren't called
  449.                     if(err == collectionItemNotFoundErr){
  450.                         err = noErr;    // we can continue if our hint isn't there. We'll get the defaults
  451.                                         // we set above
  452.                     }
  453.                 }
  454.                 DisposeHandle(myCollectionHdl);
  455.             }else
  456.                 err = MemError();
  457.                 
  458.             DisposeCollection(privateHints);
  459.         }
  460.     }    
  461. #endif
  462.  
  463.     myData = (JPEGConverterData *)psNewPtr(sizeof(JPEGConverterData));
  464.     if(myData){
  465.         unsigned char *outDataBuffer = NULL;
  466.         
  467.         bzero(myData, sizeof(JPEGConverterData));
  468.         
  469.         myData->doBinary = doBinary;
  470.         myData->doLevel2 = *languageLevel > 1;            // can we generate L2 output?
  471.         
  472.         // We MUST fill in the caller's languagelevel with what we intend to generate
  473.         *languageLevel = myData->doLevel2 ? 2 : 1;
  474.         
  475.         myData->readStream = readStream;
  476.         myData->streamIn = streamIn;
  477.         myData->streamOut = streamOut;
  478.         myData->backChannelDataBuffer = backChannelDataBuffer;
  479.         myData->backChannelDataBufferSize = backChannelDataBufferSize;
  480.  
  481.         myData->outDataBuffer = outDataBuffer = (unsigned char *)psNewPtr(DOWNLOAD_BUFFER_SIZE);
  482.  
  483.         if(outDataBuffer){
  484.             SInt32 readBytes;
  485.             short width, height, depth, numComponents;
  486.             long    hRes = 72L<<16 , vRes = 72L<<16;
  487.  
  488.             readBytes = DOWNLOAD_BUFFER_SIZE;
  489.             err = readStream->read(streamIn, (Ptr)(outDataBuffer), &readBytes);        // call the readStream input to get the first 15 data bytes
  490.             
  491.             // make sure we mark our buffer size to be either the download buffer size or the number of bytes
  492.             // read on the first read
  493.             myData->outBufferSize = readBytes;        
  494.             if(err == eofErr)err = noErr;                                // eof errors are OK. We'll just use the data we got
  495.  
  496.             if(!err){                                                    
  497.                 err = GetJPEGImageBounds(outDataBuffer, &width, &height, &depth, &hRes, &vRes, readBytes);
  498.                 if(!err){
  499.                     switch(depth){
  500.                         case 24:
  501.                             numComponents = 3;
  502.                             break;
  503.                         case 8:
  504.                             numComponents = 1;
  505.                             break;
  506.                         default:
  507.                             err = -1;
  508.                     }
  509.                 }
  510.                 if(err || !((depth == 8 || depth == 24) && width && height)){
  511.                     // this is a failure case. We can't download the data so we'll generate a log message and
  512.                     // mark it as LOGERROR instead of LOGWARNING.
  513.                     
  514.                     // note that this case shouldn't happen under normal operation since we checked the
  515.                     // data before in our converterCanConvert routine, but we'll log it
  516.                     // if it does happen                    
  517.                     err = writeLogMsg(streamOut, kSubAnon, NULL, JPEGCONVERTERSTRINGS_ID, kJPEGBadDataMsg, LOGERROR);
  518.                     // We must return an error here to stop the download
  519.                     // we need to return THIS error, even of writeLogMsg returns an error
  520.                     err = errCantHandleThisDownloadData;            // we can't handle the data, sorry
  521.                 }
  522.             }
  523.  
  524.             // if we are not doing L2, then we can't handle this data
  525.             if(!(myData->doLevel2)){
  526.                 /*     this is a failure case. We can't download the data so we'll generate a log message and
  527.                     mark it as LOGERROR instead of LOGWARNING. 
  528.  
  529.                     Note that this case shouldn't happen under normal operation since we checked the
  530.                     the language level before in our converterCanConvert routine, but we'll log it
  531.                     if it does happen. This *can* happen if the user sets up their printer and it is
  532.                     a Level 2 printer, but then they change the printer and don't re-set it up. It could
  533.                     also happen if a dumb client calls canDownload with a hints collection for one printer and 
  534.                     then calls the download routine with a hints collection for a different printer.
  535.                 */
  536.                 err = writeLogMsg(streamOut, kSubAnon, NULL, JPEGCONVERTERSTRINGS_ID, kJPEGNoLevel1SupportMsg, LOGERROR);
  537.                 // We must return an error here to stop the download
  538.                 // we need to return THIS error, even of writeLogMsg returns an error
  539.                 err = errCantHandleThisDownloadData;
  540.             }
  541.  
  542.  
  543.             if(!err){
  544.                 myData->imageheight = height;
  545.                 myData->imagewidth = width;
  546.                 myData->depth = depth;
  547.                 myData->numComponents = numComponents;
  548.             }
  549.         }else
  550.             err = MemError();
  551.  
  552.     }else
  553.         err = MemError();
  554.         
  555.     // clean up after ourselves if we have an error.
  556.     if(err){
  557.         if(myData)
  558.             psDisposePtr((Ptr)myData->outDataBuffer);
  559.         
  560.         psDisposePtr((Ptr)myData);
  561.         myData = NULL;
  562.     }
  563.  
  564.     if(clientData)
  565.         *clientData = myData;
  566.  
  567.     return err;
  568. }
  569. #undef DOWNLOAD_BUFFER_SIZE
  570.  
  571. OSStatus converterDisposeDoConvertClientData(void *clientData)
  572. {
  573.     if(clientData){        
  574.         JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
  575.     
  576.         psDisposePtr((Ptr)jpegData->outDataBuffer);
  577.         psDisposePtr((Ptr)jpegData);
  578.     }
  579.     return noErr;
  580. }
  581.  
  582.  
  583. OSStatus converterGetBBox(kHintEPSBBoxVar *bbox, void *clientData)
  584. /*    Note that this is the PostScript bounding box data that we are to return. This means
  585.     the coordinates returned should be in the default coordinate system.
  586.     
  587.     The bbox normally is: 
  588.     lowerleftX lowerleftY upperrightX upperrightY
  589.     
  590.     but we are putting this into a rect structure and that only has top, left, bottom, right
  591.     
  592.     therefore:
  593.     
  594.     top = upperrightY
  595.     left = lowerleftX
  596.     bottom = lowerleftY
  597.     right = upperrightX
  598. */
  599. {
  600.     OSStatus err = noErr;
  601.     JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
  602.     
  603.     bbox->top = jpegData->imageheight;
  604.     bbox->right = jpegData->imagewidth;
  605.     bbox->left =  bbox->bottom = 0;
  606.     
  607.     return err;
  608. }
  609.  
  610.  
  611. OSStatus converterEmitProlog(StreamInfoData comm, void *clientData)
  612. {
  613.     OSStatus err = noErr;
  614.     JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
  615.  
  616.     err = psOutPStr(comm, psPrologL2);
  617.  
  618.     // check for data coming up backchannel
  619.     if(!err)err = ReadWriteBackChannel(jpegData->streamIn, jpegData->readStream->write, jpegData->streamOut, 
  620.                     jpegData->streamOut->u.ps.read,
  621.                      jpegData->backChannelDataBuffer, jpegData->backChannelDataBufferSize);
  622.     
  623.     return err;
  624. }
  625.  
  626. OSStatus converterEmitPageData(StreamInfoData comm, void *clientData)
  627. {
  628.     OSStatus err = noErr;
  629.     JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
  630.     
  631.     // we use this boolean since we've already read the first data buffer during the conversion phase
  632.     // in our converterInitDoConvertClientData routine and we can simply use that. This allows us
  633.     // to operate without having to rewind the stream.
  634.     Boolean notFirstBuffer = false;
  635.  
  636.     short width = jpegData->imagewidth, height = jpegData->imageheight;
  637.     short depth = jpegData->depth, numComponents = jpegData->numComponents;
  638.     Boolean doBinary = jpegData->doBinary;
  639.     Boolean doLevel2 = jpegData->doLevel2;
  640.  
  641.     unsigned char *outDataBuffer = jpegData->outDataBuffer;
  642.     
  643.     PSSerialStream *readStream = jpegData->readStream;
  644.     PSStream *streamIn = jpegData->streamIn;
  645.     PSStream *streamOut = jpegData->streamOut;
  646.     UInt32 outBufferSize = jpegData->outBufferSize;
  647.     UInt32 backChannelBufferSize = jpegData->backChannelDataBufferSize;
  648.     unsigned char *backChannelBuffer = jpegData->backChannelDataBuffer;
  649.     SInt32 readBytes = outBufferSize;
  650.                                 
  651.     if(jpegData->doLevel2){
  652.         PackBitsState bitsState;
  653.         
  654.         bitsState.edgeBytes = bitsState.edgeCount = 0;
  655.         
  656.         if(!err)err = psOutPStr(comm, jpegData->doBinary ? psL2BinaryFilter: psL2ASCII85Filter);
  657.         
  658.         if(!err)err = psOutFormat(comm, psImageDictSetup1, width, height, numComponents);
  659.         if(!err)err = psOutPStr(comm, psImageDictSetup2);
  660.         if(!err)err = psOutPStr(comm, psDoImage);
  661.  
  662.         do{
  663.             if(notFirstBuffer){
  664.                 readBytes = outBufferSize;
  665.                 err = readStream->read(streamIn, outDataBuffer, &readBytes);    // call the stream input to get the first data
  666.                 if(err == eofErr)err = noErr;                                    // we'll ignore EOF errors and use readBytes of zero to indicate we're done
  667.             }else
  668.                 notFirstBuffer = true;
  669.  
  670.             if(!err && readBytes){
  671.                 if(doBinary)
  672.                     err = psOutBlock(comm, outDataBuffer, readBytes);
  673.                 else
  674.                     err = outASCII85(comm, &bitsState, outDataBuffer, readBytes, &noCompressionASCII85);
  675.             }
  676.             
  677.             // check for data coming up backchannel
  678.             if(!err)err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read,
  679.                                  backChannelBuffer, backChannelBufferSize);
  680.         }while(readBytes && !err);
  681.         
  682.         // finish the image if we are doing ascii85
  683.         if(!err && !doBinary){
  684.             err = ASCII85Done(comm, &bitsState);
  685.         }
  686.         
  687.         // once we are done drawing the page contents we return. The shell code does the
  688.         // showpage for us and completes the job.
  689.     }else{
  690.         // the !level2 case shouldn't happen here since it is checked for elsewhere but this
  691.         // is what we are going to do if it does 
  692.         err = errCantHandleThisDownloadData;
  693.     }
  694.     
  695.     return err;
  696. }
  697.  
  698. // JPEG specific code handling
  699. static OSStatus GetJPEGImageBounds(unsigned char *theDataPtr, short *width, short *height, short *depth, long *hRes, 
  700.                                         long *vRes, long validDataLength) 
  701. {
  702.     OSStatus theErr = noErr;
  703.     char    *scanData;
  704.     
  705.     *depth = 24;
  706.         
  707.     // Scan the JPEG image data and find the width, height, resolution, and depth of the image
  708.     if ( (scanData = MarkerDetect((char *)theDataPtr, width, height, hRes, vRes, depth, validDataLength)) == 0 ) {
  709.         theErr = iNotJPEGData;
  710.     }
  711.     
  712.     return theErr;
  713. } /* GetJPEGImageBounds */
  714.  
  715.  
  716. /*
  717.     
  718.  
  719.     JPEG specific stuff. Taken almost literally from the Macintosh OS SDK.
  720.     This is from the JFIF-PICT conversion program provided as a sample program 
  721.     in the QuickTime section of the SDK. The source is based originally on that shipped with the MacOS SDK 
  722.     for April 1997.
  723.     
  724.     The original change was to allow JFIF version 1.02 to be used with this code.
  725.     
  726.     DMG385:    One significant change was to fix a bug relating to how 1 component images are handled.
  727.             Also updated so that when it is about to read the 'data' value, it first looks to see if the
  728.             data is within the valid bytes of data in our buffer. If not, then we're about to read outside
  729.             the buffer and we claim that the data is not valid in that case.
  730.     
  731. */
  732.     
  733. /*
  734.  
  735.     Scan the JPEG stream for the proper markers and fill in the image parameters
  736.     
  737.     returns NULL if it cant comprehend the data, otherwise a pointer to the start
  738.     of the JPEG data.
  739.     
  740.     
  741.     It does a cursory check on the JPEG data to see if it's reasonable.
  742.     Check out the ISO JPEG spec if you really want to know what's going on here.
  743.     
  744. */
  745.  
  746. #define invalidData(data, endBuffer)    ((data) >= (endBuffer))                // DMG385
  747.  
  748. static char *
  749. MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth, long validBytes)            // DMG385
  750. {
  751.     short    frame_field_length;
  752.     short    data_precision;
  753.     short    scan_field_length;
  754.     short    number_component,scan_components;
  755.     short    c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
  756.     short    dac_t1, dac_t2, dac_t3;
  757.     unsigned char    c;
  758.     short    qtabledefn;
  759.     short    htabledefn;
  760.     short    status;
  761.     short    length;
  762.     short    i;
  763.     char *bufferEnd = data + validBytes;
  764.     
  765.     // check before we get started
  766.     if(invalidData(data, bufferEnd))return (0);                                            // DMG385
  767.  
  768.     c = *data++;
  769.     qtabledefn = 0;
  770.     htabledefn = 0;
  771.     status = 0;
  772.     while (c != (unsigned char)MARKER_SOS) {
  773.  
  774.         // check after every 'continue' of our loop
  775.         if(invalidData(data, bufferEnd))return (0);                                            // DMG385
  776.  
  777.         while (c != (unsigned char)MARKER_PREFIX){
  778.  
  779.             // check as we are scanning the buffer for MARKER_PREFIX
  780.             if(invalidData(data, bufferEnd))return (0);                                        // DMG385
  781.  
  782.             c = *data++;                        /* looking for marker prefix bytes */
  783.         }
  784.         while (c == (unsigned char)MARKER_PREFIX){
  785.  
  786.             // check as we are scanning the buffer for multiple MARKER_PREFIX
  787.             if(invalidData(data, bufferEnd))return (0);                                            // DMG385
  788.  
  789.             c = *data++;                        /* (multiple?) marker prefix bytes */
  790.         }
  791.         if (c == 0)
  792.             continue;                                    /* 0 is never a marker code */
  793.  
  794.         if (c == (unsigned char)MARKER_SOF) {
  795.  
  796.             frame_field_length = *(short *)data;
  797.             data += 2;
  798.             data_precision = *data++;
  799.             
  800.             if ( data_precision != 8 ) { 
  801.                 status = 2;
  802.             }
  803.  
  804.             *height = *(short *)data;
  805.             data += 2;
  806.             *width = *(short *)data;
  807.             data += 2;
  808.                         
  809.             number_component = *data++;
  810.             
  811.             switch ( number_component  ) {
  812.             case 3:
  813.                 c1 = *data++;
  814.                 hv1 = *data++;
  815.                 q1 = *data++;
  816.                 c2 = *data++;
  817.                 hv2 = *data++;
  818.                 q2 = *data++;
  819.                 c3 = *data++;
  820.                 hv3 = *data++;
  821.                 q3 = *data++;
  822.                 *depth = 24;                        // DMG385 changed from 32 to 24 since this is 3 component
  823.                 break;
  824.             case 1:        
  825.                 c1 = *data++;
  826.                 hv1 = *data++;
  827.                 q1 = *data++;
  828.                 *depth = 8;                            // DMG385 changed from 40 to 8 since this is 1 component
  829.                 break;
  830.             default:
  831.                 status = 3;
  832.                 break;
  833.             }
  834.             continue;
  835.         }
  836.     
  837.         if (c == (unsigned char)MARKER_SOS) {
  838.             short tn;
  839.             scan_field_length = *(short *)data;
  840.             data += 2;
  841.             scan_components = *data++;
  842.  
  843.             if( *depth == 8 && scan_components == 1){                                            // begin DMG385
  844.                 // careful to initialize these for one component case
  845.                 dac_t1 = dac_t2 = dac_t3 = 0;
  846.             }                                                                                    // end DMG385
  847.  
  848.             // make sure that scan_components was valid since we are going to loop over it
  849.             if(invalidData(data, bufferEnd))return (0);                                            // DMG385
  850.  
  851.             for ( i=0; i < scan_components; i++ ) {
  852.                 unsigned char cn,dac_t;
  853.                 
  854.                 cn = *data++;
  855.                 dac_t = *data++;
  856.                 if ( cn == c1 ) {
  857.                     dac_t1 = dac_t;
  858.                 } else if ( cn == c2 ) {
  859.                     dac_t2 = dac_t;
  860.                 } else if ( cn == c3 ) {
  861.                     dac_t3 = dac_t;
  862.                 } else {    
  863.                     status = 29;
  864.                     break;
  865.                 }
  866.             }
  867.             switch ( tn=(dac_t1 & 0xf) )  {
  868.             case 0:
  869.             case 1:
  870.                 break;
  871.             case 0xf:
  872.                 break;
  873.             default:
  874.                 status = 33;
  875.                 break;
  876.             }
  877.             switch (  tn=(dac_t2 & 0xf) )  {
  878.             case 0:
  879.             case 1:
  880.                 break;
  881.             case 0xf:
  882.                 break;
  883.             default:
  884.                 status = 33;
  885.                 break;
  886.             }
  887.             switch (  tn=(dac_t3 & 0xf) )  {
  888.             case 0:
  889.             case 1:
  890.                 break;
  891.             case 0xf:
  892.                 break;
  893.             default:
  894.                 status = 33;
  895.                 break;
  896.             }
  897.  
  898.  
  899.             /*  Initialize the DC tables */
  900.             
  901.             switch (  tn=dac_t1 & 0xf0 )  {
  902.             case 0:
  903.             case 0x10:
  904.                 break;
  905.             case 0xf0:
  906.                 break;
  907.             default:
  908.                 status = 34;
  909.                 break;
  910.             }
  911.             switch (  tn=dac_t2 & 0xf0 )  {
  912.             case 0:
  913.             case 0x10:
  914.                 break;
  915.             case 0xf0:
  916.                 break;
  917.             default:
  918.                 status = 34;
  919.                 break;
  920.             }
  921.             switch (  tn=dac_t3 & 0xf0 )  {
  922.             case 0:
  923.             case 0x10:
  924.                 break;
  925.             case 0xf0:
  926.                 break;
  927.             default:
  928.                 status = 34;
  929.                 break;
  930.             }
  931.             if ( *data++ != 0 )  {
  932. //                status = 18;
  933.             }
  934.             if ( *data++ != 63 )  {
  935. //                status = 19;
  936.             }
  937.             if ( *data++ != 0 ) {
  938. //                status = 20;
  939.             }
  940.             if ( status )
  941.                 return(0);
  942.             else
  943.                 return(data);
  944.         }
  945.  
  946.         if (c == (unsigned char)MARKER_DQT) {
  947.             scan_field_length = *(short *)data;
  948.  
  949. /*    DMG385    removed call to SwallowQuantTable since there was no verification being done 
  950.             and the way we skipped it was to look at the size anyway
  951. */
  952. //DMG385    SwallowQuantTable(data, bufferEnd);
  953.             data += scan_field_length;
  954.             continue;
  955.         }
  956.         if (c == (unsigned char)MARKER_DHT) {
  957.             scan_field_length = *(short *)data;
  958.  
  959. /*    DMG385    removed call to SwallowHuffTable since there was no verification being done 
  960.             and the way we skipped it was to look at the size anyway
  961. */
  962. //DMG385    SwallowHuffTable(data, bufferEnd);
  963.             data += scan_field_length;                // DMG385 added since we want to skip over the data and this wasn't here!
  964.             continue;
  965.         }
  966.         if (c == (unsigned char)MARKER_DRI) {
  967.             length = *(short *)data;            /* read length */
  968.             data += 2;
  969.             length = *(short *)data;            
  970.             data += 2;
  971.             continue;
  972.         }
  973.         if (c == (unsigned char)MARKER_DNL) {
  974.             length = *(short *)data;            /* read length */
  975.             data += 2;
  976.             length = *(short *)data;            
  977.             data += 2;
  978.             continue;
  979.         }
  980.         if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
  981.             continue;
  982.         }
  983.  
  984.         if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI)    /* image start, end marker */
  985.             continue;
  986.  
  987.         if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
  988.             status = 12;
  989.             length = *(short *)data;            /* read length */
  990.             data += length;
  991.             continue;
  992.         }
  993.         if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
  994.             length = *(short *)data;            /* read length */
  995.  
  996.             // DMG385 Even though we get a length value, we don't loop over it so we don't need to check here.
  997.             // We'll just wait until the next continue in the while
  998.  
  999.             data += 2;
  1000.             length -= 2;
  1001.             if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
  1002.                 char buf[5];
  1003.                 buf[0] = *data++;
  1004.                 buf[1] = *data++;
  1005.                 buf[2] = *data++;
  1006.                 buf[3] = *data++;
  1007.                 buf[4] = *data++;
  1008.                 length -= 5;
  1009.                 
  1010.                 if ( buf[0] == 'J' && buf[1] == 'F'  && buf[2] == 'I'  && buf[3] == 'F' ) {
  1011.                     short    units;
  1012.                     long    xres,yres;
  1013.                     short    version;
  1014.                     
  1015.                     
  1016.                     version = *(short *)data; data += 2;length -= 2;
  1017.                     if ( version != 0x100 && version != 0x101 && version != 0x102 ) { // DMG - added version 102
  1018.                         status = 44;        // unknown JFIF version
  1019.                         break;
  1020.                     }
  1021.                     units = *data++; length--;
  1022.                     xres = *(short *)data; data += 2; length -= 2;
  1023.                     yres = *(short *)data; data += 2; length -= 2;
  1024.  
  1025.                     switch ( units ) {
  1026.                     case 0:            // no res, just aspect ratio
  1027.                         *hRes = FixMul(72L<<16,xres<<16);
  1028.                         *vRes = FixMul(72L<<16,yres<<16);
  1029.                         break;
  1030.                     case 1:            // dots per inch
  1031.                         *hRes = xres<<16;
  1032.                         *vRes = yres<<16;
  1033.                         break;
  1034.                     case 2:            // dots per centimeter (we convert to dpi )
  1035.                         *hRes = FixMul(0x28a3d,xres<<16);
  1036.                         *vRes = FixMul(0x28a3d,xres<<16);
  1037.                         break;    
  1038.                     default:
  1039.                         break;
  1040.                     }
  1041.                     xres = *data++; length--;
  1042.                     yres = *data++; length--;
  1043.                     
  1044.                     /* skip JFIF thumbnail */
  1045.                     
  1046.                     xres *= yres;
  1047.                     data += xres*3; length -= xres*3;
  1048.                     
  1049.                     if (  length != 0 ) {
  1050.                         status = 44;        // bad jfif marker
  1051.                         break;
  1052.                     }
  1053.                 }
  1054.             }
  1055.             data += length;
  1056.             continue;
  1057.         }
  1058.         if (c == (unsigned char)MARKER_COM) {
  1059.             length = *(short *)data;            /* read length */
  1060.             data += length;
  1061.             continue;
  1062.         }
  1063.         if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
  1064.             length = *(short *)data;            /* read length */
  1065.             data += length;
  1066.             continue;
  1067.         }
  1068.         if ( c == 0x1 )
  1069.             continue;
  1070.         if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
  1071.             length = *(short *)data;            /* read length */
  1072.             status = 13;
  1073.             data += length;
  1074.             continue;
  1075.         }
  1076.     }
  1077.     return(0);
  1078. }
  1079.  
  1080.  
  1081. /*
  1082.     Read the quantization table from the JPEG bitstream.
  1083. */
  1084.  
  1085. static void 
  1086. SwallowQuantTable(char *data, char *bufferEnd)                                            // DMG385
  1087. {
  1088.     long    i;
  1089.     long    length,pm,nm;
  1090.  
  1091.     length = *(short *)data;            /* read length */
  1092.  
  1093.     // make sure that length was valid since we are going to loop over it
  1094.     if(invalidData(data, bufferEnd))return;                                            // DMG385
  1095.  
  1096.     length -= 2;
  1097.     data += 2;
  1098.     while ( length ) {
  1099.         nm= *data++;                    /* read precision and number */
  1100.         pm = nm>>4;    
  1101.         nm &= 0xf;
  1102.         length--;
  1103.         if ( pm ) {
  1104.             for(i=0;i<64;i++) {
  1105.                 length -= 2;
  1106.                 data += 2;
  1107.             }
  1108.         } else {
  1109.             for(i=0;i<64;i++) {
  1110.                 length--;
  1111.                 data++;
  1112.             }
  1113.         }
  1114.     }    
  1115. }
  1116.  
  1117. /*
  1118.     Read the huffman table from the JPEG bitstream.
  1119. */
  1120.  
  1121. static void 
  1122. SwallowHuffTable(char *data, char *bufferEnd)                        // DMG385
  1123. {
  1124.     short    i,tc,id;
  1125.     long    length;
  1126.     
  1127.     unsigned char    bin[17];
  1128.     unsigned char    val[256];
  1129.  
  1130.     bin[0] = 0;
  1131.     length = *(short *)data;            /* read length */
  1132.  
  1133.     // make sure that length was valid since we are going to loop over it
  1134.     if(invalidData(data, bufferEnd))return;                                            // DMG385
  1135.  
  1136.     data += 2;
  1137.     length -= 2;
  1138.     while ( length ) {
  1139.         id=*data++;                /* read id */
  1140.         length--;
  1141.         if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
  1142.             return;
  1143.         }
  1144.         tc = 0;
  1145.         for(i=0;i<16;i++) {
  1146.             length--;
  1147.             tc += (bin[i+1] = *data++);
  1148.         }
  1149.  
  1150.         // make sure that tc was valid since we are going to loop over it
  1151.         if(invalidData(data, bufferEnd))return;                                        // DMG385
  1152.  
  1153.         for (i=0; i < tc; i++ ) {
  1154.             length--;
  1155.             val[i] = *data++;
  1156.         }
  1157.     }
  1158. }
  1159.  
  1160.  
  1161. // ASCII85 handling
  1162.  
  1163. static void noCompressionASCII85 (Ptr *src, Ptr *dst, long count)
  1164. {
  1165.     BlockMove(*src, *dst, count);
  1166.     *src += count;
  1167.     *dst += count;
  1168. }
  1169.  
  1170. static OSStatus ASCII85Done(StreamInfoData comm, PackBitsState *bitsState)
  1171. {
  1172.     unsigned long     edgeBytes = bitsState->edgeBytes;
  1173.     unsigned short     word = 0x800D;
  1174.     OSErr             err = noErr;
  1175.     unsigned char     ASCIIBuf[10], *c = ASCIIBuf;
  1176.     int                edgeCount = bitsState->edgeCount;
  1177.     
  1178.     edgeBytes &= (unsigned long)-1 << 8*(4 - edgeCount);
  1179.     edgeBytes |= 0x80000000 >> 8*edgeCount;
  1180.     c = pack5(c, edgeBytes) - (3 - edgeCount);
  1181.     *c++ = '~';
  1182.     *c++ = '>';
  1183.     *c++ = '\015';
  1184.     err = psOutBlock(comm, ASCIIBuf, c - ASCIIBuf);    
  1185.     bitsState->edgeCount = 0;
  1186.     bitsState->edgeBytes = 0;
  1187.         
  1188.     return err;
  1189. }
  1190.  
  1191. static StringPtr pack5(StringPtr c, unsigned long data) {
  1192.     unsigned long divisor;
  1193.     int n;
  1194.     
  1195.     divisor = 85L*85L*85L*85L;
  1196.     for (n = 5; n; n--) {
  1197.         *c++ = data / divisor + '!';
  1198.         data %= divisor;
  1199.         divisor /= 85;
  1200.     }
  1201.     
  1202.     return c;
  1203. }
  1204.  
  1205. // data for ASCII85 routines
  1206. #define BUFFERSIZE 128
  1207. #define ASCIIBUFFERSIZE (BUFFERSIZE*5)/4+1  /* add 1 for newline */
  1208. static OSStatus outASCII85(StreamInfoData comm, PackBitsState *bitsState, unsigned char *block, long nBytes, compressionProcPtr compPtr)
  1209. {
  1210.     unsigned long     binBytes, binBytesCount, bytesOut, *binLongPtr;
  1211.     OSStatus             err = noErr;
  1212.     unsigned char    *asciiBytes;
  1213.     unsigned char     *bin, binBuf[BUFFERSIZE], ASCIIBuf[ASCIIBUFFERSIZE];
  1214.     int                edgeCount, edgeBytes;
  1215.     
  1216.     edgeCount = bitsState->edgeCount;
  1217.     edgeBytes = bitsState->edgeBytes;
  1218.     while (nBytes && !err) {
  1219.         if (nBytes > (BUFFERSIZE-1)-edgeCount)
  1220.             bytesOut = (BUFFERSIZE-1)-edgeCount;
  1221.         else
  1222.             bytesOut = nBytes;
  1223.         nBytes -= bytesOut;
  1224.         *(long *)binBuf = edgeBytes;
  1225.         bin = binBuf + edgeCount;
  1226.         
  1227.         (*compPtr)((Ptr *)&block, (Ptr *)&bin, bytesOut);
  1228.         
  1229.         binBytesCount = bin - binBuf;
  1230.         binLongPtr = (unsigned long *)binBuf;
  1231.         asciiBytes = ASCIIBuf;
  1232.         if (binBytesCount > 3) {
  1233.             while (binBytesCount > 3) {
  1234.                 binBytes = *binLongPtr++;
  1235.                 binBytesCount -= 4;
  1236.                 if (binBytes == 0)
  1237.                     *asciiBytes++ = 'z';
  1238.                 else
  1239.                     asciiBytes = pack5(asciiBytes, binBytes);
  1240.             }
  1241.             if (nBytes == 0)
  1242.                 *asciiBytes++ = '\015';
  1243.             err = psOutBlock(comm, ASCIIBuf, asciiBytes - ASCIIBuf);
  1244.         }
  1245.         edgeBytes = *binLongPtr;
  1246.         edgeCount = (char)binBytesCount;
  1247.     }
  1248.     bitsState->edgeCount = edgeCount;
  1249.     bitsState->edgeBytes = edgeBytes;
  1250.     
  1251.     return err;
  1252. }
  1253. #undef BUFFERSIZE
  1254. #undef ASCIIBUFFERSIZE
  1255.  
  1256.  
  1257.